﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

using ChangeListLibrary;
using CodeReviewApplication.Models;

namespace CodeReviewApplication.Views
{
    public static class ViewHelper
    {
        public readonly static IEnumerable<SelectListItem> m_reviewStatusList = GetList(typeof(ReviewStatusEnum));
        public readonly static IEnumerable<SelectListItem> m_commentThreadStatusList = GetList(typeof(CommentThreadStatusEnum));
        public readonly static IEnumerable<SelectListItem> m_userReviewStatusList = GetList(typeof(UserReviewStatusEnum));
        public readonly static IEnumerable<SelectListItem> m_changeListItemTypeList = GetList(typeof(ChangeTypeEnum));

        public static IDictionary<DiffTypeEnum, string> m_diffTypeClasses = GetDiffTypeClasses();
        public static IDictionary<UserReviewStatusEnum, string> m_userReviewStatusClasses = GetUserReviewStatusClasses();
        public static IDictionary<ReviewStatusEnum, string> m_reviewStatusClasses = GetReviewStatusClasses();
        public static IDictionary<CommentThreadStatusEnum, string> m_commentThreadStatusClasses = GetCommentThreadStatusClasses();
        public static IDictionary<ChangeListItemNodeStatusTypeEnum, string> m_changeListItemNodeStatusTypeClasses = GetChangeListItemNodeStatusTypeClasses();
        public static IDictionary<RegistrationRequestStatusEnum, string> m_registrationRequestStatusClasses = GetRegistrationRequestStatusClasses();

        public static IDictionary<DiffTypeEnum, string> GetDiffTypeClasses()
        {
            IDictionary<DiffTypeEnum, string> classes = new Dictionary<DiffTypeEnum, string>();

            KeyValuePair<DiffTypeEnum, string>[] pairs = { 
                new KeyValuePair<DiffTypeEnum, string>(DiffTypeEnum.Added, "dst-added"),
                new KeyValuePair<DiffTypeEnum, string>(DiffTypeEnum.Removed, "dst-removed"),
                new KeyValuePair<DiffTypeEnum, string>(DiffTypeEnum.Same, "dst-same"),
                };

            foreach (KeyValuePair<DiffTypeEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IDictionary<UserReviewStatusEnum, string> GetUserReviewStatusClasses()
        {
            IDictionary<UserReviewStatusEnum, string> classes = new Dictionary<UserReviewStatusEnum, string>();

            KeyValuePair<UserReviewStatusEnum, string>[] pairs = { 
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.NotApplicable, "urs-not-applicable"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.NotStarted, "urs-not-started"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.InReview, "urs-in-review"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.Completed, "urs-completed"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.CompletedWithComments, "urs-completed-with-comments"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.Declined, "urs-declined"),
                new KeyValuePair<UserReviewStatusEnum, string>(UserReviewStatusEnum.Cancelled, "urs-cancelled"),
                };

            foreach (KeyValuePair<UserReviewStatusEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IDictionary<ReviewStatusEnum, string> GetReviewStatusClasses()
        {
            IDictionary<ReviewStatusEnum, string> classes = new Dictionary<ReviewStatusEnum, string>();

            KeyValuePair<ReviewStatusEnum, string>[] pairs = { 
                new KeyValuePair<ReviewStatusEnum, string>(ReviewStatusEnum.NotStarted, "rs-not-started"),
                new KeyValuePair<ReviewStatusEnum, string>(ReviewStatusEnum.InReview, "rs-in-review"),
                new KeyValuePair<ReviewStatusEnum, string>(ReviewStatusEnum.Completed, "rs-completed"),
                new KeyValuePair<ReviewStatusEnum, string>(ReviewStatusEnum.Cancelled, "rs-cancelled"),
                new KeyValuePair<ReviewStatusEnum, string>(ReviewStatusEnum.Closed, "rs-closed"),
                };

            foreach (KeyValuePair<ReviewStatusEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IDictionary<CommentThreadStatusEnum, string> GetCommentThreadStatusClasses()
        {
            IDictionary<CommentThreadStatusEnum, string> classes = new Dictionary<CommentThreadStatusEnum, string>();

            KeyValuePair<CommentThreadStatusEnum, string>[] pairs = { 
                new KeyValuePair<CommentThreadStatusEnum, string>(CommentThreadStatusEnum.Open, "cts-open"),
                new KeyValuePair<CommentThreadStatusEnum, string>(CommentThreadStatusEnum.Closed, "cts-closed"),
                new KeyValuePair<CommentThreadStatusEnum, string>(CommentThreadStatusEnum.Migrated, "cts-migrated"),
                new KeyValuePair<CommentThreadStatusEnum, string>(CommentThreadStatusEnum.Latest, "cts-latest"),
                new KeyValuePair<CommentThreadStatusEnum, string>(CommentThreadStatusEnum.All, "cts-all"),
                };

            foreach (KeyValuePair<CommentThreadStatusEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IDictionary<RegistrationRequestStatusEnum, string> GetRegistrationRequestStatusClasses()
        {
            IDictionary<RegistrationRequestStatusEnum, string> classes = new Dictionary<RegistrationRequestStatusEnum, string>();

            KeyValuePair<RegistrationRequestStatusEnum, string>[] pairs = { 
                new KeyValuePair<RegistrationRequestStatusEnum, string>(RegistrationRequestStatusEnum.NotProcessed, "rrs-notprocessed"),
                new KeyValuePair<RegistrationRequestStatusEnum, string>(RegistrationRequestStatusEnum.Approved, "rrs-approved"),
                new KeyValuePair<RegistrationRequestStatusEnum, string>(RegistrationRequestStatusEnum.Denied, "rrs-denied"),
                };

            foreach (KeyValuePair<RegistrationRequestStatusEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IDictionary<ChangeListItemNodeStatusTypeEnum, string> GetChangeListItemNodeStatusTypeClasses()
        {
            IDictionary<ChangeListItemNodeStatusTypeEnum, string> classes = new Dictionary<ChangeListItemNodeStatusTypeEnum, string>();

            KeyValuePair<ChangeListItemNodeStatusTypeEnum, string>[] pairs = { 
                new KeyValuePair<ChangeListItemNodeStatusTypeEnum, string>(ChangeListItemNodeStatusTypeEnum.Superseeded, "clt-superseeded"),
                new KeyValuePair<ChangeListItemNodeStatusTypeEnum, string>(ChangeListItemNodeStatusTypeEnum.Latest, "clt-latest"),
                };

            foreach (KeyValuePair<ChangeListItemNodeStatusTypeEnum, string> pair in pairs)
            {
                classes.Add(pair);
            }

            return classes;
        }

        public static IEnumerable<SelectListItem> GetList(Type enumType)
        {
            List<SelectListItem> list = new List<SelectListItem>();

            foreach (object value in Enum.GetValues(enumType))
            {
                SelectListItem item = new SelectListItem();

                item.Value = ((Enum)value).ToString("d");
                item.Text = Enum.GetName(enumType, value);

                list.Add(item);
            }

            return list;
        }

        public static string Bold(
            this HtmlHelper helper,
            string input
            )
        {
            return string.Format("<b>{0}</b>", input);
        }

        public static string ReviewsLink(
            this HtmlHelper helper,
            string linkText,
            string routeName
            )
        {
            return helper.FullRouteLink(linkText, routeName, null);
        }

        public static string ReviewStatusDropDown(
            this HtmlHelper helper,
            string id,
            Review review
            )
        {
            List<SelectListItem> list = new List<SelectListItem>(m_reviewStatusList);

            string value = helper.ReviewStatus(review.StatusEnum);

            list.Single(item => (item.Text == value)).Selected = true;

            return helper.DropDownList(id, list);
        }

        public static string ReviewStatus(
            this HtmlHelper helper,
            Review review
            )
        {
            return helper.ReviewStatus(review.StatusEnum);
        }

        public static string ReviewStatus(
            this HtmlHelper helper,
            ReviewStatusEnum status
            )
        {
            string result = string.Empty;

            switch (status)
            {
                case ReviewStatusEnum.NotStarted:
                    result = "Not Started";

                    break;

                case ReviewStatusEnum.InReview:
                    result = "In Review";

                    break;

                case ReviewStatusEnum.Completed:
                    result = "Completed";

                    break;

                case ReviewStatusEnum.Cancelled:
                    result = "Cancelled";

                    break;

                case ReviewStatusEnum.Closed:
                    result = "Closed";

                    break;

                default:
                    throw new NotImplementedException();
            }

            return result;
        }

        public static string ReviewStatusHtml(
            this HtmlHelper helper,
            ReviewView review,
            string userId
            )
        {
            ReviewUserRole reviewer = review.Review.GetRole(
                userId,
                UserRoleEnum.Reviewer
                );
            
            if (reviewer == null)
            {
                return helper.ReviewStatusHtml(
                    review.Review.StatusEnum
                    );
            }

            return string.Format(
                "<span>{0} | {1}</span>",
                helper.ReviewStatusHtml(review.Review.StatusEnum),
                helper.UserReviewStatusHtml(reviewer.StatusEnum)
                );
        }

        public static string ReviewStatusHtml(
            this HtmlHelper helper,
            ReviewView review
            )
        {
            return helper.ReviewStatusHtml(
                review.Review.StatusEnum
                );
        }

        public static string ReviewStatusHtml(
            this HtmlHelper helper,
            ReviewStatusEnum status
            )
        {
            return string.Format(
                "<span class='{0}'>'{1}'</span>",
                m_reviewStatusClasses[status],
                helper.ReviewStatus(status)
                );
        }

        public static string UserReviewStatusDropDown(
            this HtmlHelper helper,
            string id,
            ReviewView review
            )
        {
            List<SelectListItem> list = new List<SelectListItem>(m_reviewStatusList);

            string value = helper.UserReviewStatus(review.Status);

            list.Single(item => (item.Text == value)).Selected = true;

            return helper.DropDownList(id, list);
        }

        public static string UserReviewStatus(
            this HtmlHelper helper,
            ReviewView review
            )
        {
            return helper.UserReviewStatus(review.Status);
        }

        public static string UserReviewStatus(
            this HtmlHelper helper,
            UserReviewStatusEnum status
            )
        {
            string result = string.Empty;

            switch (status)
            {
                case UserReviewStatusEnum.NotStarted:
                    result = "Not Started";

                    break;

                case UserReviewStatusEnum.NotApplicable:
                    result = "Not Applicable";

                    break;
                
                case UserReviewStatusEnum.InReview:
                    result = "In Review";

                    break;
                
                case UserReviewStatusEnum.Declined:
                    result = "Declined";

                    break;
                
                case UserReviewStatusEnum.CompletedWithComments:
                    result = "Completed with Comments";

                    break;
                
                case UserReviewStatusEnum.Completed:
                    result = "Completed";

                    break;
                
                case UserReviewStatusEnum.Cancelled:
                    result = "Cancelled";

                    break;

                default:
                    throw new NotImplementedException();
            }

            return result;
        }

        public static string UserReviewStatusHtml(
            this HtmlHelper helper,
            ReviewView review
            )
        {
            return helper.UserReviewStatusHtml(
                review.Status
                );
        }

        public static string UserReviewStatusHtml(
            this HtmlHelper helper,
            UserReviewStatusEnum status
            )
        {
            return string.Format(
                "<span class='{0}'>'{1}'</span>",
                m_userReviewStatusClasses[status],
                helper.UserReviewStatus(status)
                );
        }

        public static string CommentThreadStatusDropDown(
            this HtmlHelper helper,
            string id,
            CommentThread thread
            )
        {
            List<SelectListItem> list = new List<SelectListItem>(m_commentThreadStatusList);

            string value = helper.CommentThreadStatus(thread.StatusEnum);

            list.Single(item => (item.Text == value)).Selected = true;

            return helper.DropDownList(id, list);
        }

        public static string CommentThreadStatus(
            this HtmlHelper helper,
            CommentThread thread
            )
        {
            return helper.CommentThreadStatus(thread.StatusEnum);
        }

        public static string CommentThreadStatus(
            this HtmlHelper helper,
            CommentThreadStatusEnum status
            )
        {
            return status.ToString();
        }

        public static string CommentThreadStatusHtml(
            this HtmlHelper helper,
            CommentThread thread
            )
        {
            return helper.CommentThreadStatusHtml(
                thread.StatusEnum
                );
        }

        public static string CommentThreadStatusHtml(
            this HtmlHelper helper,
            CommentThreadStatusEnum status,
            string className
            )
        {
            return string.Format(
                "<span class='{0}'>{1}</span>",
                className,
                status.ToString()
                );
        }

        public static string CommentThreadStatusHtml(
            this HtmlHelper helper,
            CommentThreadStatusEnum status
            )
        {
            return helper.CommentThreadStatusHtml(
                status,
                m_commentThreadStatusClasses[status]
                );
        }

        public static string CommentThreadStatusHtml(
            this HtmlHelper helper,
            CommentThreadStatusEnum status,
            IEnumerable<CommentThread> threads
            )
        {
            bool derivedState = false;

            switch (status)
            {
                case CommentThreadStatusEnum.All:
                case CommentThreadStatusEnum.Latest:
                    derivedState = true;
                    
                    break;
            }

            if (!derivedState || !threads.Any())
            {
                return helper.CommentThreadStatusHtml(status);
            }

            IEnumerable<CommentThread> openThreads = threads.Where(
                thread => thread.IsInState(CommentThreadStatusEnum.Open)
                );

            IEnumerable<CommentThread> closedThreads = threads.Where(
                thread => thread.IsInState(CommentThreadStatusEnum.Closed)
                );

            CommentThreadStatusEnum derivedStatus = status;

            if (openThreads.Any())
            {
                derivedStatus = CommentThreadStatusEnum.Open;
            }
            else if (closedThreads.Any())
            {
                derivedStatus = CommentThreadStatusEnum.Closed;
            }

            return helper.CommentThreadStatusHtml(
                status,
                m_commentThreadStatusClasses[derivedStatus]
                );
        }

        public static string CommentThreadLocation(
            this HtmlHelper helper,
            CommentThread commentThread
            )
        {
            bool oldSimilar = (
                commentThread.OldStartLineNumber == commentThread.OldEndLineNumber
                );

            bool newSimilar = (
                commentThread.NewStartLineNumber == commentThread.NewEndLineNumber
                );

            bool oldNewSimilar = (
                (commentThread.OldStartLineNumber == commentThread.NewStartLineNumber) &&
                (commentThread.OldEndLineNumber == commentThread.NewEndLineNumber)
                );

            string oldLocation = string.Empty;

            if (oldSimilar)
            {
                oldLocation = string.Format(
                    "({0})",
                    commentThread.OldStartLineNumber
                    );
            }
            else
            {
                oldLocation = string.Format(
                    "({0}-{1})",
                    commentThread.OldStartLineNumber,
                    commentThread.OldEndLineNumber
                    );
            }

            if (oldNewSimilar)
            {
                return oldLocation;
            }

            string newLocation = string.Empty;

            if (!oldNewSimilar)
            {
                if (newSimilar)
                {
                    newLocation = string.Format(
                        "({0})",
                        commentThread.NewStartLineNumber
                        );
                }
                else
                {
                    newLocation = string.Format(
                        "({0}-{1})",
                        commentThread.NewStartLineNumber,
                        commentThread.NewEndLineNumber
                        );
                }
            }

            return string.Format("{0}, {1}", oldLocation, newLocation);
        }

        public static string CommentThreadNewLink(
            this HtmlHelper helper,
            ReviewView review,
            string userId
            )
        {
            StringBuilder builder = new StringBuilder();

            if (review.Review.IsReviewer(userId))
            {
                builder.Append(
                    helper.FullRouteLink(
                        "New Comment Thread",
                        "CommentThreadsCreate",
                        null,                            
                        new { id = "CommentThreadNew" }
                        )
                    );
            }

            return builder.ToString();
        }

        public static string GetClass(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            return m_changeListItemNodeStatusTypeClasses[changeListItem.StatusEnum];
        }

        public static string GetSourceName(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            StringBuilder builder = new StringBuilder();

            switch (changeListItem.TypeEnum)
            {
                case ChangeTypeEnum.Delete:
                case ChangeTypeEnum.Edit:
                case ChangeTypeEnum.Branch:

                    builder.AppendFormat(
                        "{0}#{1}",
                        helper.Encode(changeListItem.Name),
                        helper.Encode(changeListItem.Version)
                        );

                    break;
            }

            return builder.ToString();
        }

        public static string GetDestinationName(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            StringBuilder builder = new StringBuilder();

            switch (changeListItem.TypeEnum)
            {
                case ChangeTypeEnum.Add:
                case ChangeTypeEnum.Branch:

                    builder.AppendFormat(
                        "{0}#{1}",
                        helper.Encode(changeListItem.NewName),
                        helper.Encode(changeListItem.NewVersion)
                        );

                    break;

                case ChangeTypeEnum.Delete:
                case ChangeTypeEnum.Edit:

                    builder.AppendFormat(
                        "{0}#{1}",
                        helper.Encode(changeListItem.Name),
                        helper.Encode(changeListItem.NewVersion)
                        );

                    break;
            }

            return builder.ToString();
        }

        public static string EncodeTypeImg(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            string verticalAlign
            )
        {
            return string.Format(
                "<img style='height:16px;width:16px;vertical-align:{0}' title='{2}' src='/Content/Images/{1}{2}.gif' />",
                verticalAlign,
                (changeListItem.IsVirtual ? "virtual" : string.Empty),                    
                changeListItem.TypeEnum.ToString()
                );
        }

        public static string EncodeTypeImg(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            return helper.EncodeTypeImg(changeListItem, "top");
        }

        public static string EncodeType(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            return string.Format(
                "<span style='white-space:pre'>{0} {1}</span>",
                helper.EncodeTypeImg(changeListItem),
                changeListItem.TypeEnum.ToString()
                );
        }

        public static string EncodeType(
            this HtmlHelper helper,
            FileTypeEnum fileType
            )
        {
            return helper.Encode(fileType.ToString());
        }

        public static string CommentThreadsLink(
            this HtmlHelper helper,
            Review review,
            CommentThreadStatusEnum status
            )
        {
            int threadsCount = review.GetCommentThreads(
                helper.ViewContext.HttpContext.User.Identity.Name,
                status
                ).Count();

            if (threadsCount > 0)
            {
                return helper.FullRouteLink(
                    threadsCount.ToString(),
                    "ReviewCommentThreads",
                    new
                    {
                        reviewId = review.Id,
                        status = status
                    }
                    );
            }

            return helper.Encode(threadsCount.ToString());
        }

        private delegate string FormatCommentThreadsLink(
            IEnumerable<CommentThread> threads,
            CommentThreadStatusEnum status
            );

        private static string CommentThreadsLink(
            HtmlHelper helper,
            IEnumerable<CommentThread> threads,
            FormatCommentThreadsLink formatLink
            )
        {
            int threadsCount = threads.Count();

            if (threadsCount > 0)
            {
                CommentThreadStatusEnum[] statusArray = {
                    CommentThreadStatusEnum.Latest,
                    CommentThreadStatusEnum.Open,
                    CommentThreadStatusEnum.Closed,
                    CommentThreadStatusEnum.Migrated,
                    CommentThreadStatusEnum.All
                    };

                Dictionary<CommentThreadStatusEnum, IEnumerable<CommentThread>> statusMap =
                    CommentThread.CreateMap(threads);

                StringBuilder builder = new StringBuilder();

                foreach (CommentThreadStatusEnum status in statusArray)
                {
                    if (!CommentThread.IsVisible(statusMap, status))
                    {
                        continue;
                    }

                    IEnumerable<CommentThread> localThreads = statusMap[status];

                    if (builder.Length > 0)
                    {
                        builder.Append(", ");
                    }

                    builder.AppendFormat(
                        "{0} ({1})",
                        helper.CommentThreadStatusHtml(
                            status,
                            localThreads
                            ),
                        formatLink(localThreads, status)
                        );
                }

                return builder.ToString();
            }

            return helper.Encode(threadsCount.ToString());
        }

        public static string CommentThreadsLink(
            this HtmlHelper helper,
            Review review
            )
        {
            return CommentThreadsLink(
                helper,
                review.GetCommentThreads(
                    helper.ViewContext.HttpContext.User.Identity.Name
                    ),
                delegate (
                    IEnumerable<CommentThread> threads,
                    CommentThreadStatusEnum status
                    )
                {
                    return helper.FullRouteLink(
                        threads.Count().ToString(),
                        "ReviewCommentThreads",
                        new
                        {
                            reviewId = review.Id,
                            status = status
                        }
                        );
                }
                );
        }

        public static string CommentThreadsLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            CommentThreadStatusEnum status
            )
        {
            int threadsCount = changeList.GetCommentThreads(
                helper.ViewContext.HttpContext.User.Identity.Name,
                status
                ).Count();

            if (threadsCount > 0)
            {
                return helper.FullRouteLink(
                    threadsCount.ToString(),
                    "ChangeListCommentThreads",
                    new
                    {
                        changeListId = changeList.Id,
                        status = status
                    }
                    );
            }

            return helper.Encode(threadsCount.ToString());
        }

        public static string CommentThreadsLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList
            )
        {
            return CommentThreadsLink(
                helper,
                changeList.GetCommentThreads(
                    helper.ViewContext.HttpContext.User.Identity.Name
                    ),
                delegate(
                    IEnumerable<CommentThread> threads,
                    CommentThreadStatusEnum status
                    )
                {
                    return helper.FullRouteLink(
                        threads.Count().ToString(),
                        "ChangeListCommentThreads",
                        new
                        {
                            changeListId = changeList.Id,
                            status = status
                        }
                        );
                }
                );
        }

        public static string EncodeDiffSummary(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            string diffStat = string.Empty;
            string title = string.Empty;

            if (changeListItem.FileTypeEnum == FileTypeEnum.Binary)
            {
                diffStat = string.Format(
                    "<span class='{0} diffstat'>N/A</span>",
                    m_diffTypeClasses[DiffTypeEnum.Same]
                    );

                title = "Not Applicable";
            }
            else if (changeListItem.LinesTotal > 0)
            {
                List<string> diffStats = new List<string>();
                List<string> titleParts = new List<string>();

                if (changeListItem.LinesAdded > 0)
                {
                    diffStats.Add(
                        string.Format(
                            "<span class='{0} diffstat'>+{1}</span>",
                            m_diffTypeClasses[DiffTypeEnum.Added],
                            changeListItem.LinesAdded
                            )
                        );

                    titleParts.Add(
                        string.Format("Added: {0}", changeListItem.LinesAdded)
                        );
                }

                if (changeListItem.LinesRemoved > 0)
                {
                    diffStats.Add(
                        string.Format(
                            "<span class='{0} diffstat'>-{1}</span>",
                            m_diffTypeClasses[DiffTypeEnum.Removed],
                            changeListItem.LinesRemoved
                            )
                        );

                    titleParts.Add(
                        string.Format("Removed: {0}", changeListItem.LinesRemoved)
                        );
                }

                if (changeListItem.LinesSame > 0)
                {
                    diffStats.Add(
                        string.Format(
                            "<span class='{0} diffstat'>{1}</span>",
                            m_diffTypeClasses[DiffTypeEnum.Same],
                            changeListItem.LinesSame
                            )
                        );

                    titleParts.Add(
                        string.Format("Similar: {0}", changeListItem.LinesSame)
                        );
                }

                diffStat = string.Join(", ", diffStats.ToArray());
                title = string.Join(", ", titleParts.ToArray());
            }
            else
            {
                diffStat = string.Format(
                    "<span class='{0}'>None</span>",
                    m_diffTypeClasses[DiffTypeEnum.Same]
                    );

                title = "None";
            }

            return string.Format(
                "<div class='diffstats' lineschanged='{0}' title='{1}'>{2}</div>",
                changeListItem.LinesChanged,
                title,
                diffStat
                );
        }

        public static string CommentThreadsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            CommentThreadStatusEnum status
            )
        {
            int threadsCount = changeListItem.GetCommentThreads(
                helper.ViewContext.HttpContext.User.Identity.Name,
                status
                ).Count();

            if (threadsCount > 0)
            {
                return helper.FullRouteLink(
                    threadsCount.ToString(),
                    "ChangeCommentThreads",
                    new
                    {
                        changeListItemId = changeListItem.Id,
                        status = status
                    }
                    );
            }

            return helper.Encode(threadsCount.ToString());
        }
        
        public static string CommentThreadsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            return CommentThreadsLink(
                helper,
                changeListItem.GetCommentThreads(
                    helper.ViewContext.HttpContext.User.Identity.Name
                    ),
                delegate(
                    IEnumerable<CommentThread> threads,
                    CommentThreadStatusEnum status
                    )
                {
                    return helper.FullRouteLink(
                        threads.Count().ToString(),
                        "ChangeCommentThreads",
                        new
                        {
                            changeListItemId = changeListItem.Id,
                            status = status
                        }
                        );
                }
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            Review review,
            string description
            )
        {
            return helper.FullRouteLink(
                description,
                "ReviewDetails",
                new { reviewId = review.Id },
                new { @class = "autoopen" }
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            Review review
            )
        {
            return helper.DetailsLink(review, review.Title);
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList
            )
        {
            return helper.DetailsLink(changeList, changeList.VisibleFullDepotId);
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            UserDepot depot
            )
        {
            string link = string.Empty;

            if (!changeList.IsVirtual)
            {
                if (depot != null)
                {
                    link = helper.DetailsLink(depot);
                }
                else
                {
                    link = changeList.DepotName;
                }
            }
            else
            {
                link = "None";
            }

            return link;
        }

        public static string DownloadLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            string userId
            )
        {
            string downloadFileName = (
                changeList.IsVirtual ? "None" : changeList.GetDownloadFileName()
                );

            if (changeList.CanDownload(userId))
            {
                return helper.FullRouteLink(
                    downloadFileName,
                    "ChangeListsDownload",
                    new
                    {
                        changeListId = changeList.Id,
                        fileName = downloadFileName
                    }
                    );
            }

            return downloadFileName;
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            string description
            )
        {
            if (changeList.IsVirtual)
            {
                description = "Virtual";
            }

            return helper.FullRouteLink(
                description,
                "ChangeListDetails",
                new { changeListId = changeList.Id },
                new { @class = "autoopen" }
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            if (changeListItem.TypeEnum != ChangeTypeEnum.Branch)
            {
                return helper.DetailsLink(
                    changeListItem,
                    changeListItem.DetailedName
                    );
            }

            return string.Format(
                "{0},<br/>{1}",
                helper.DetailsLink(
                    changeListItem,
                    changeListItem.DetailedSourceName,
                    true
                    ),
                helper.DetailsLink(
                    changeListItem,
                    changeListItem.DetailedDestinationName,
                    false
                    )
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            string description
            )
        {
            return helper.DetailsLink(changeListItem, description, true);
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            string description,
            bool enableAutoOpen
            )
        {
            bool autoOpen = (
                enableAutoOpen &&
                changeListItem.Different &&
                (changeListItem.FileTypeEnum == FileTypeEnum.Text)
                );

            return helper.FullRouteLink(
                description,
                "ChangeDetails",
                new { changeListItemId = changeListItem.Id },
                new 
                { 
                    @class = autoOpen? "autoopen" : string.Empty
                }
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            UserDepot depot
            )
        {
            return helper.FullRouteLink(
                depot.Name,
                "DepotsDetails",
                new { depotName = depot.Name },
                new { @class = "autoopen" }
                );    
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            Int64 scrollToLineNumber,
            string description
            )
        {
            return helper.FullRouteLink(
                description,
                "ChangeDetails",
                new 
                { 
                    changeListItemId = changeListItem.Id,
                    scrollToLineNumber = scrollToLineNumber
                },
                new
                {
                    @class = (
                        ((changeListItem.FileTypeEnum == FileTypeEnum.Text) &&
                          changeListItem.Different) ? "autoopen" : ""
                        )
                }
                );
        }

        public static string AlternateDiffLink(
            this HtmlHelper helper,
            FileDiffResultModel diffResultModel,
            string description
            )
        {
            return helper.FullRouteLink(
                description,
                "ChangeDetails",
                new { 
                    changeListItemId = diffResultModel.ChangeListItemObject.Id,
                    changeListItemViewType = ChangeListItemViewTypeEnum.Details,
                    diffViewType = diffResultModel.AlternateDiffViewType,
                    scrollToLineNumber = diffResultModel.ScrollToLineNumber
                    }
                );
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            CommentThread thread
            )
        {
            return helper.DetailsLink(thread, thread.LinkText);
        }

        public static string DetailsLink(
            this HtmlHelper helper,
            CommentThread thread,
            string description
            )
        {
            string autoOpenClass = (
                thread.IsInState(CommentThreadStatusEnum.Migrated) ?
                string.Empty : "autoopen"
                );

            return helper.FullRouteLink(
                description,
                "CommentThreadDetails",
                new { threadId = thread.Id },
                new { @class = autoOpenClass }
                );
        }

        static Regex m_urlRegEx = new Regex(
            @"((www\.|(http|https|ftp|news|file)+\:\/\/)[&#95;.a-z0-9-]*[a-z0-9\/&#95;:@=.+?,##%&~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])",
            (RegexOptions.IgnoreCase | RegexOptions.Compiled)
            );

        public static string InsertLinks(
            this HtmlHelper helper,
            string htmlText
            )
        {
            return m_urlRegEx.Replace(
                htmlText, 
                "<a href=\"$1\" target=\"&#95;blank\">$1</a>"
                ).Replace("href=\"www", "href=\"http://www");
        }

        public static string GetSourceDiffClass(
            this HtmlHelper helper,
            FileDiffResultDiffLine line
            )
        {
            if (line.DiffType == DiffTypeEnum.Removed)
            {
                return "dt-removed";
            }

            return string.Empty;
        }

        public static string GetDestinationDiffClass(
            this HtmlHelper helper,
            FileDiffResultDiffLine line
            )
        {
            if (line.DiffType == DiffTypeEnum.Added)
            {
                return "dt-added";
            }

            return string.Empty;
        }

        public static string GetDiffClass(
            this HtmlHelper helper,
            FileDiffResultDiffLine line
            )
        {
            string diffClass = string.Empty;

            if (line.DiffType != DiffTypeEnum.Same)
            {
                switch (line.DiffType)
                {
                    case DiffTypeEnum.Removed:
                        diffClass = "dt-removed";

                        break;

                    case DiffTypeEnum.Added:
                        diffClass = "dt-added";

                        break;
                }
            }

            return diffClass;
        }

        public static string GetDiffRangeClass(
            this HtmlHelper helper,
            FileDiffResultDiffLine currentLine,
            FileDiffResultDiffLine previousLine
            )
        {
            string diffClass = "";

            if (previousLine == null)
            {
                switch (currentLine.DiffType)
                {
                    case DiffTypeEnum.Same:
                        break;

                    default:
                        diffClass = "diffstart";

                        break;
                }
            }
            else
            {
                switch (currentLine.DiffType)
                {
                    case DiffTypeEnum.Same:
                        break;

                    default:
                        if (currentLine.DiffType != previousLine.DiffType)
                        {
                            diffClass = "diffstart";
                        }

                        break;
                }
            }

            return diffClass;
        }

        public static string GetTitle(
            this HtmlHelper helper,
            Review review
            )
        {
            return string.Format("Review: '{0}'", review.Title);
        }

        public static string GetTitle(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList
            )
        {
            return string.Format("Changelist: '{0}'", changeList.VisibleFullDepotId);
        }

        public static string GetTitle(
            this HtmlHelper helper,
            ChangeListItem changeListItem
            )
        {
            return string.Format("Change: '{0}'", changeListItem.Title);
        }

        public static string GetTitle(
            this HtmlHelper helper,
            CommentThread commentThread
            )
        {
            return string.Format("Comment Thread: '{0}'", commentThread.LinkText);
        }

        public static string GetTitle(
            this HtmlHelper helper,
            Comment comment
            )
        {
            return string.Format("Comment: '{0}'", comment.Description);
        }

        public static string GetTitle(
            this HtmlHelper helper,
            UserDepot depot
            )
        {
            return string.Format("Depot: '{0}'", depot.Name);
        }

        public static string DeleteLink(
            this HtmlHelper helper,
            Comment comment,
            string userId
            )
        {
            string result = "";

            if (comment.CanDelete(userId))
            {
                result = helper.FullRouteLink(
                    "Delete",
                    "CommentDelete",
                    new { commentId = comment.Id },
                    new { @class = "commentdeletelink", @title = helper.GetTitle(comment) }
                    );
            }

            return result;
        }


        public static string DeleteLink(
            this HtmlHelper helper,
            CommentThread thread,
            string userId
            )
        {
            string result = "";

            if (thread.CanDelete(userId))
            {
                result = helper.FullRouteLink(
                    "Delete",
                    "CommentThreadDelete",
                    new { threadId = thread.Id },
                    new { @class = "commentthreaddeletelink", @title = helper.GetTitle(thread) }
                    );
            }

            return result;
        }

        public static string DeleteLink(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            string userId
            )
        {
            string result = "";

            if (changeList.CanDelete(userId))
            {
                result = helper.FullRouteLink(
                    "Delete",
                    "ChangeListDelete",
                    new { changeListId = changeList.Id },
                    new { @class = "changelistdeletelink", @title = helper.GetTitle(changeList) }
                    );
            }

            return result;
        }

        public static string DeleteLink(
            this HtmlHelper helper,
            Review review,
            string userId
            )
        {
            string result = "";

            if (review.CanDelete(userId))
            {
                result = helper.FullRouteLink(
                    "Delete",
                    "ReviewDelete",
                    new { reviewId = review.Id },
                    new { @class = "reviewdeletelink", @title = helper.GetTitle(review) }
                    );
            }

            return result;
        }

        public static string DeleteLink(
            this HtmlHelper helper,
            UserDepot depot,
            string userId
            )
        {
            string result = "";

            if (depot.CanDelete(userId))
            {
                result = helper.FullRouteLink(
                    "Delete",
                    "DepotsDelete",
                    new { depotName = depot.Name },
                    new { @class = "depotdeletelink", @title = helper.GetTitle(depot) }
                    );
            }

            return result;
        }

        public static string SearchLink(
            this HtmlHelper helper,
            SearchResult searchResult
            )
        {
            string result = "";

            Review review;
            CodeReviewApplication.Models.ChangeList changeList;
            ChangeListItem changeListItem;
            CommentThread thread;

            switch (searchResult.Result.ObjectTypeEnum)
            {
                case ObjectTypeEnum.Review:
                    review = (Review)searchResult.ActualObject;

                    result = helper.DetailsLink(
                        review,
                        helper.GetTitle(review)
                        );

                    break;

                case ObjectTypeEnum.ChangeList:
                    changeList = (
                        CodeReviewApplication.Models.ChangeList
                        )searchResult.ActualObject;

                    result = helper.DetailsLink(
                        changeList,
                        helper.GetTitle(changeList)
                        );

                    break;

                case ObjectTypeEnum.ChangeListItem:
                    changeListItem = (ChangeListItem)searchResult.ActualObject;

                    result = helper.DetailsLink(
                        changeListItem,
                        helper.GetTitle(changeListItem)
                        );
                    
                    break;

                case ObjectTypeEnum.CommentThread:
                    thread = (CommentThread)searchResult.ActualObject;

                    result = helper.DetailsLink(
                        thread,
                        helper.GetTitle(thread) 
                        );
                    
                    break;

                default:
                    break;
            }

            return result;
        }

        public static string SearchContent(
            this HtmlHelper helper,
            Search searchResult,
            int maxLines
            )
        {
            StringBuilder builder = new StringBuilder();

            int linesLeft = maxLines;

            StreamReader reader = new StreamReader(
                new MemoryStream(searchResult.Content.ToArray())
                );

            while (linesLeft > 0)
            {
                string readLine = reader.ReadLine();

                if (readLine == null)
                {
                    break;
                }

                builder.Append(readLine);

                linesLeft--;
            }

            reader.Close();

            return builder.ToString();
        }

        static public string FullActionLink(
            this HtmlHelper helper,
            string linkText,
            string actionName
            )
        {
            return helper.FullActionLink(
                linkText,
                actionName,
                null
                );
        }

        static public string FullActionLink(
            this HtmlHelper helper,
            string linkText,
            string actionName,
            string controllerName
            )
        {
            return helper.ActionLink(
                linkText,
                actionName,
                controllerName,
                helper.ViewContext.HttpContext.Request.Url.Scheme,
                helper.ViewContext.HttpContext.Request.Url.Host,
                string.Empty,
                null,
                null
                );
        }

        static public string FullActionLink(
            this HtmlHelper helper,
            string linkText,
            string actionName,
            string controllerName,
            object routeValues
            )
        {
            return helper.ActionLink(
                linkText,
                actionName,
                controllerName,
                helper.ViewContext.HttpContext.Request.Url.Scheme,
                helper.ViewContext.HttpContext.Request.Url.Host,
                string.Empty,
                routeValues,
                null
                );
        }

        static public string FullRouteLink(
            this HtmlHelper helper,
            string linkText,
            string routeName
            )
        {
            return helper.FullRouteLink(
                linkText,
                routeName,
                null,
                null
                );
        }

        static public string FullRouteLink(
            this HtmlHelper helper,
            string linkText,
            string routeName,
            object routeValues
            )
        {
            return helper.FullRouteLink(
                linkText,
                routeName,
                routeValues,
                null
                );
        }

        static public string FullRouteLink(
            this HtmlHelper helper,
            string linkText,
            string routeName,
            object routeValues,
            object htmlAttributes
            )
        {
            return helper.RouteLink(
                linkText,
                routeName,
                helper.ViewContext.HttpContext.Request.Url.Scheme,
                helper.ViewContext.HttpContext.Request.Url.Host,
                string.Empty,
                routeValues,
                htmlAttributes
                );
        }

        static public string Encode(
            this HtmlHelper helper,
            UserDepot depot,
            Dictionary<string, string> settings,
            SourceControlManager.SettingProperties setting
            )
        {
            string value = (
                settings.ContainsKey(setting.Name) ?
                settings[setting.Name] : string.Empty
                );

            if (!setting.IsPassword)
            {
                return helper.Encode(value);
            }

            return new string('\u25CF', value.Length);
        }

        static public string EncodeInput(
            this HtmlHelper helper,
            UserDepot depot,
            Dictionary<string, string> settings,
            SourceControlManager.SettingProperties setting
            )
        {
            string boxType = setting.IsPassword ? "password" : "text";
            string settingClasses = string.Format(
                "setting {0}",
                (setting.IsRequired ? "setting-required" : "setting-optional")
                );

            string settingValue = (
                settings.ContainsKey(setting.Name) ?
                settings[setting.Name] : string.Empty
                );

            return helper.TextBox(
                setting.Name,
                settingValue,
                new { type = boxType, @class = settingClasses }
                );
        }

        static public string SourceLineNumber(
            this HtmlHelper helper,
            FileDiffResultDiffLine diffLine
            )
        {
            if (diffLine.DiffType != DiffTypeEnum.Added)
            {
                return diffLine.SourceLineNumber.ToString();
            }

            return string.Empty;
        }

        static public string DestinationLineNumber(
            this HtmlHelper helper,
            FileDiffResultDiffLine diffLine
            )
        {
            if (diffLine.DiffType != DiffTypeEnum.Removed)
            {
                return diffLine.DestinationLineNumber.ToString();
            }

            return string.Empty;
        }

        public static string RegistrationRequestStatusHtml(
            this HtmlHelper helper,
            RegistrationRequestStatusEnum status
            )
        {
            return string.Format(
                "<span class='{0}'>{1}</span>",
                m_registrationRequestStatusClasses[status],
                status.ToString()
                );
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            RegistrationRequest registrationRequest
            )
        {
            return registrationRequest.Actions.ToString().Replace(", ", " ");
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            Review review,
            string userId
            )
        {
            return review.GetActions(userId).ToString().Replace(", ", " ");
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            CodeReviewApplication.Models.ChangeList changeList,
            string userId
            )
        {
            return changeList.GetActions(userId).ToString().Replace(", ", " ");
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            ChangeListItem changeListItem,
            string userId
            )
        {
            return changeListItem.GetActions(userId).ToString().Replace(", ", " ");
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            CommentThread commentThread,
            string userId
            )
        {
            return commentThread.GetActions(userId).ToString().Replace(", ", " ");
        }

        public static string GetActionClasses(
            this HtmlHelper helper,
            UserDepot depot,
            string userId
            )
        {
            return depot.GetActions(userId).ToString().Replace(", ", " ");
        }

        public static string EditLink(
            this HtmlHelper helper,
            Review review,
            string userId
            )
        {
            if (review.IsPropertiesEditable(userId))
            {
                return helper.FullRouteLink(
                    "HiddenLink",
                    "ReviewEdit",
                    new { reviewId = review.Id },
                    new { @style = "display:none", @class = "editlink" }
                    );
            }

            return string.Empty;
        }

        public static string EditLink(
            this HtmlHelper helper,
            UserDepot depot,
            string userId
            )
        {
            if (depot.CanEdit(userId))
            {
                return helper.FullRouteLink(
                    "HiddenLink",
                    "DepotsEdit",
                    new { depotName = depot.Name },
                    new { @style = "display:none", @class = "editlink" }
                    );
            }

            return string.Empty;
        }
    }
}
